home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / pyshared / PIL / ImageOps.py < prev    next >
Text File  |  2006-12-03  |  13KB  |  409 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id: ImageOps.py 2760 2006-06-19 13:31:40Z fredrik $
  4. #
  5. # standard image operations
  6. #
  7. # History:
  8. # 2001-10-20 fl   Created
  9. # 2001-10-23 fl   Added autocontrast operator
  10. # 2001-12-18 fl   Added Kevin's fit operator
  11. # 2004-03-14 fl   Fixed potential division by zero in equalize
  12. # 2005-05-05 fl   Fixed equalize for low number of values
  13. #
  14. # Copyright (c) 2001-2004 by Secret Labs AB
  15. # Copyright (c) 2001-2004 by Fredrik Lundh
  16. #
  17. # See the README file for information on usage and redistribution.
  18. #
  19.  
  20. import Image
  21. import operator
  22.  
  23. ##
  24. # (New in 1.1.3) The <b>ImageOps</b> module contains a number of
  25. # 'ready-made' image processing operations.  This module is somewhat
  26. # experimental, and most operators only work on L and RGB images.
  27. #
  28. # @since 1.1.3
  29. ##
  30.  
  31. #
  32. # helpers
  33.  
  34. def _border(border):
  35.     if type(border) is type(()):
  36.         if len(border) == 2:
  37.             left, top = right, bottom = border
  38.         elif len(border) == 4:
  39.             left, top, right, bottom = border
  40.     else:
  41.         left = top = right = bottom = border
  42.     return left, top, right, bottom
  43.  
  44. def _color(color, mode):
  45.     if Image.isStringType(color):
  46.         import ImageColor
  47.         color = ImageColor.getcolor(color, mode)
  48.     return color
  49.  
  50. def _lut(image, lut):
  51.     if image.mode == "P":
  52.         # FIXME: apply to lookup table, not image data
  53.         raise NotImplementedError("mode P support coming soon")
  54.     elif image.mode in ("L", "RGB"):
  55.         if image.mode == "RGB" and len(lut) == 256:
  56.             lut = lut + lut + lut
  57.         return image.point(lut)
  58.     else:
  59.         raise IOError, "not supported for this image mode"
  60.  
  61. #
  62. # actions
  63.  
  64. ##
  65. # Maximize (normalize) image contrast.  This function calculates a
  66. # histogram of the input image, removes <i>cutoff</i> percent of the
  67. # lightest and darkest pixels from the histogram, and remaps the image
  68. # so that the darkest pixel becomes black (0), and the lightest
  69. # becomes white (255).
  70. #
  71. # @param image The image to process.
  72. # @param cutoff How many percent to cut off from the histogram.
  73. # @param ignore The background pixel value (use None for no background).
  74. # @return An image.
  75.  
  76. def autocontrast(image, cutoff=0, ignore=None):
  77.     "Maximize image contrast, based on histogram"
  78.     histogram = image.histogram()
  79.     lut = []
  80.     for layer in range(0, len(histogram), 256):
  81.         h = histogram[layer:layer+256]
  82.         if ignore is not None:
  83.             # get rid of outliers
  84.             try:
  85.                 h[ignore] = 0
  86.             except TypeError:
  87.                 # assume sequence
  88.                 for ix in ignore:
  89.                     h[ix] = 0
  90.         if cutoff:
  91.             # cut off pixels from both ends of the histogram
  92.             # get number of pixels
  93.             n = 0
  94.             for ix in range(256):
  95.                 n = n + h[ix]
  96.             # remove cutoff% pixels from the low end
  97.             cut = n * cutoff / 100
  98.             for lo in range(256):
  99.                 if cut > h[lo]:
  100.                     cut = cut - h[lo]
  101.                     h[lo] = 0
  102.                 else:
  103.                     h[lo] = h[lo] - cut
  104.                     cut = 0
  105.                 if cut <= 0:
  106.                     break
  107.             # remove cutoff% samples from the hi end
  108.             cut = n * cutoff / 100
  109.             for hi in range(255, -1, -1):
  110.                 if cut > h[hi]:
  111.                     cut = cut - h[hi]
  112.                     h[hi] = 0
  113.                 else:
  114.                     h[hi] = h[hi] - cut
  115.                     cut = 0
  116.                 if cut <= 0:
  117.                     break
  118.         # find lowest/highest samples after preprocessing
  119.         for lo in range(256):
  120.             if h[lo]:
  121.                 break
  122.         for hi in range(255, -1, -1):
  123.             if h[hi]:
  124.                 break
  125.         if hi <= lo:
  126.             # don't bother
  127.             lut.extend(range(256))
  128.         else:
  129.             scale = 255.0 / (hi - lo)
  130.             offset = -lo * scale
  131.             for ix in range(256):
  132.                 ix = int(ix * scale + offset)
  133.                 if ix < 0:
  134.                     ix = 0
  135.                 elif ix > 255:
  136.                     ix = 255
  137.                 lut.append(ix)
  138.     return _lut(image, lut)
  139.  
  140. ##
  141. # Colorize grayscale image.  The <i>black</i> and <i>white</i>
  142. # arguments should be RGB tuples; this function calculates a colour
  143. # wedge mapping all black pixels in the source image to the first
  144. # colour, and all white pixels to the second colour.
  145. #
  146. # @param image The image to colourize.
  147. # @param black The colour to use for black input pixels.
  148. # @param white The colour to use for white input pixels.
  149. # @return An image.
  150.  
  151. def colorize(image, black, white):
  152.     "Colorize a grayscale image"
  153.     assert image.mode == "L"
  154.     black = _color(black, "RGB")
  155.     white = _color(white, "RGB")
  156.     red = []; green = []; blue = []
  157.     for i in range(256):
  158.         red.append(black[0]+i*(white[0]-black[0])/255)
  159.         green.append(black[1]+i*(white[1]-black[1])/255)
  160.         blue.append(black[2]+i*(white[2]-black[2])/255)
  161.     image = image.convert("RGB")
  162.     return _lut(image, red + green + blue)
  163.  
  164. ##
  165. # Remove border from image.  The same amount of pixels are removed
  166. # from all four sides.  This function works on all image modes.
  167. #
  168. # @param image The image to crop.
  169. # @param border The number of pixels to remove.
  170. # @return An image.
  171. # @see Image#Image.crop
  172.  
  173. def crop(image, border=0):
  174.     "Crop border off image"
  175.     left, top, right, bottom = _border(border)
  176.     return image.crop(
  177.         (left, top, image.size[0]-right, image.size[1]-bottom)
  178.         )
  179.  
  180. ##
  181. # Deform the image.
  182. #
  183. # @param image The image to deform.
  184. # @param deformer A deformer object.  Any object that implements a
  185. #     <b>getmesh</b> method can be used.
  186. # @param resample What resampling filter to use.
  187. # @return An image.
  188.  
  189. def deform(image, deformer, resample=Image.BILINEAR):
  190.     "Deform image using the given deformer"
  191.     return image.transform(
  192.         image.size, Image.MESH, deformer.getmesh(image), resample
  193.         )
  194.  
  195. ##
  196. # Equalize the image histogram.  This function applies a non-linear
  197. # mapping to the input image, in order to create a uniform
  198. # distribution of grayscale values in the output image.
  199. #
  200. # @param image The image to equalize.
  201. # @param mask An optional mask.  If given, only the pixels selected by
  202. #     the mask are included in the analysis.
  203. # @return An image.
  204.  
  205. def equalize(image, mask=None):
  206.     "Equalize image histogram"
  207.     if image.mode == "P":
  208.         image = image.convert("RGB")
  209.     h = image.histogram(mask)
  210.     lut = []
  211.     for b in range(0, len(h), 256):
  212.         histo = filter(None, h[b:b+256])
  213.         if len(histo) <= 1:
  214.             lut.extend(range(256))
  215.         else:
  216.             step = (reduce(operator.add, histo) - histo[-1]) / 255
  217.             if not step:
  218.                 lut.extend(range(256))
  219.             else:
  220.                 n = step / 2
  221.                 for i in range(256):
  222.                     lut.append(n / step)
  223.                     n = n + h[i+b]
  224.     return _lut(image, lut)
  225.  
  226. ##
  227. # Add border to the image
  228. #
  229. # @param image The image to expand.
  230. # @param border Border width, in pixels.
  231. # @param fill Pixel fill value (a colour value).  Default is 0 (black).
  232. # @return An image.
  233.  
  234. def expand(image, border=0, fill=0):
  235.     "Add border to image"
  236.     left, top, right, bottom = _border(border)
  237.     width = left + image.size[0] + right
  238.     height = top + image.size[1] + bottom
  239.     out = Image.new(image.mode, (width, height), _color(fill, image.mode))
  240.     out.paste(image, (left, top))
  241.     return out
  242.  
  243. ##
  244. # Returns a sized and cropped version of the image, cropped to the
  245. # requested aspect ratio and size.
  246. # <p>
  247. # The <b>fit</b> function was contributed by Kevin Cazabon.
  248. #
  249. # @param size The requested output size in pixels, given as a
  250. #     (width, height) tuple.
  251. # @param method What resampling method to use.  Default is Image.NEAREST.
  252. # @param bleed Remove a border around the outside of the image (from all
  253. #     four edges.  The value is a decimal percentage (use 0.01 for one
  254. #     percent).  The default value is 0 (no border).
  255. # @param centering Control the cropping position.  Use (0.5, 0.5) for
  256. #     center cropping (e.g. if cropping the width, take 50% off of the
  257. #     left side, and therefore 50% off the right side).  (0.0, 0.0)
  258. #     will crop from the top left corner (i.e. if cropping the width,
  259. #     take all of the crop off of the right side, and if cropping the
  260. #     height, take all of it off the bottom).  (1.0, 0.0) will crop
  261. #     from the bottom left corner, etc. (i.e. if cropping the width,
  262. #     take all of the crop off the left side, and if cropping the height
  263. #     take none from the top, and therefore all off the bottom).
  264. # @return An image.
  265.  
  266. def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
  267.     """
  268.     This method returns a sized and cropped version of the image,
  269.     cropped to the aspect ratio and size that you request.
  270.     """
  271.  
  272.     # by Kevin Cazabon, Feb 17/2000
  273.     # kevin@cazabon.com
  274.     # http://www.cazabon.com
  275.  
  276.     # ensure inputs are valid
  277.     if type(centering) != type([]):
  278.         centering = [centering[0], centering[1]]
  279.  
  280.     if centering[0] > 1.0 or centering[0] < 0.0:
  281.         centering [0] = 0.50
  282.     if centering[1] > 1.0 or centering[1] < 0.0:
  283.         centering[1] = 0.50
  284.  
  285.     if bleed > 0.49999 or bleed < 0.0:
  286.         bleed = 0.0
  287.  
  288.     # calculate the area to use for resizing and cropping, subtracting
  289.     # the 'bleed' around the edges
  290.  
  291.     # number of pixels to trim off on Top and Bottom, Left and Right
  292.     bleedPixels = (
  293.         int((float(bleed) * float(image.size[0])) + 0.5),
  294.         int((float(bleed) * float(image.size[1])) + 0.5)
  295.         )
  296.  
  297.     liveArea = (
  298.         bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1,
  299.         image.size[1] - bleedPixels[1] - 1
  300.         )
  301.  
  302.     liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1])
  303.  
  304.     # calculate the aspect ratio of the liveArea
  305.     liveAreaAspectRatio = float(liveSize[0])/float(liveSize[1])
  306.  
  307.     # calculate the aspect ratio of the output image
  308.     aspectRatio = float(size[0]) / float(size[1])
  309.  
  310.     # figure out if the sides or top/bottom will be cropped off
  311.     if liveAreaAspectRatio >= aspectRatio:
  312.         # liveArea is wider than what's needed, crop the sides
  313.         cropWidth = int((aspectRatio * float(liveSize[1])) + 0.5)
  314.         cropHeight = liveSize[1]
  315.     else:
  316.         # liveArea is taller than what's needed, crop the top and bottom
  317.         cropWidth = liveSize[0]
  318.         cropHeight = int((float(liveSize[0])/aspectRatio) + 0.5)
  319.  
  320.     # make the crop
  321.     leftSide = int(liveArea[0] + (float(liveSize[0]-cropWidth) * centering[0]))
  322.     if leftSide < 0:
  323.         leftSide = 0
  324.     topSide = int(liveArea[1] + (float(liveSize[1]-cropHeight) * centering[1]))
  325.     if topSide < 0:
  326.         topSide = 0
  327.  
  328.     out = image.crop(
  329.         (leftSide, topSide, leftSide + cropWidth, topSide + cropHeight)
  330.         )
  331.  
  332.     # resize the image and return it
  333.     return out.resize(size, method)
  334.  
  335. ##
  336. # Flip the image vertically (top to bottom).
  337. #
  338. # @param image The image to flip.
  339. # @return An image.
  340.  
  341. def flip(image):
  342.     "Flip image vertically"
  343.     return image.transpose(Image.FLIP_TOP_BOTTOM)
  344.  
  345. ##
  346. # Convert the image to grayscale.
  347. #
  348. # @param image The image to convert.
  349. # @return An image.
  350.  
  351. def grayscale(image):
  352.     "Convert to grayscale"
  353.     return image.convert("L")
  354.  
  355. ##
  356. # Invert (negate) the image.
  357. #
  358. # @param image The image to invert.
  359. # @return An image.
  360.  
  361. def invert(image):
  362.     "Invert image (negate)"
  363.     lut = []
  364.     for i in range(256):
  365.         lut.append(255-i)
  366.     return _lut(image, lut)
  367.  
  368. ##
  369. # Flip image horizontally (left to right).
  370. #
  371. # @param image The image to mirror.
  372. # @return An image.
  373.  
  374. def mirror(image):
  375.     "Flip image horizontally"
  376.     return image.transpose(Image.FLIP_LEFT_RIGHT)
  377.  
  378. ##
  379. # Reduce the number of bits for each colour channel.
  380. #
  381. # @param image The image to posterize.
  382. # @param bits The number of bits to keep for each channel (1-8).
  383. # @return An image.
  384.  
  385. def posterize(image, bits):
  386.     "Reduce the number of bits per color channel"
  387.     lut = []
  388.     mask = ~(2**(8-bits)-1)
  389.     for i in range(256):
  390.         lut.append(i & mask)
  391.     return _lut(image, lut)
  392.  
  393. ##
  394. # Invert all pixel values above a threshold.
  395. #
  396. # @param image The image to posterize.
  397. # @param threshold All pixels above this greyscale level are inverted.
  398. # @return An image.
  399.  
  400. def solarize(image, threshold=128):
  401.     "Invert all values above threshold"
  402.     lut = []
  403.     for i in range(256):
  404.         if i < threshold:
  405.             lut.append(i)
  406.         else:
  407.             lut.append(255-i)
  408.     return _lut(image, lut)
  409.